/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * <http://www.mongodb.com/licensing/server-side-public-license>.
 */
import * as React from 'react';
import { render, waitFor, screen } from 'wrappedTestingLibrary';
import userEvent from '@testing-library/user-event';

import selectEvent from 'helpers/selectEvent';
import MockStore from 'helpers/mocking/StoreMock';
import Widget from 'views/logic/widgets/Widget';
import { createElasticsearchQueryString } from 'views/logic/queries/Query';
import TestStoreProvider from 'views/test/TestStoreProvider';
import useViewsPlugin from 'views/test/testViewsPlugin';
import { updateWidget } from 'views/logic/slices/widgetActions';
import { executeActiveQuery } from 'views/logic/slices/viewSlice';

import EditWidgetFrame from './EditWidgetFrame';

import WidgetContext from '../contexts/WidgetContext';

jest.mock('views/logic/fieldtypes/useFieldTypes');
jest.mock('hooks/useHotkey', () => jest.fn());

jest.mock('views/stores/StreamsStore', () => ({
  StreamsStore: MockStore([
    'getInitialState',
    () => ({
      streams: [
        { title: 'PFLog', id: '5c2e27d6ba33a9681ad62775' },
        { title: 'DNS Logs', id: '5d2d9649e117dc4df84cf83c' },
      ],
    }),
  ]),
}));

jest.mock('moment', () => {
  const mockMoment = jest.requireActual('moment');

  return Object.assign(() => mockMoment('2019-10-10T12:26:31.146Z'), mockMoment);
});

jest.mock('views/logic/slices/viewSlice', () => ({
  ...jest.requireActual('views/logic/slices/viewSlice'),
  executeActiveQuery: jest.fn(() => async () => {}),
}));

jest.mock('views/logic/slices/widgetActions', () => ({
  ...jest.requireActual('views/logic/slices/widgetActions'),
  updateWidget: jest.fn(() => async () => {}),
}));

describe('EditWidgetFrame', () => {
  describe('on a dashboard', () => {
    const widget = Widget.builder()
      .id('deadbeef')
      .type('dummy')
      .query(createElasticsearchQueryString())
      .timerange({ type: 'relative', from: 300 })
      .config({})
      .build();
    const renderSUT = (props?: Partial<React.ComponentProps<typeof EditWidgetFrame>>) =>
      render(
        <TestStoreProvider>
          <WidgetContext.Provider value={widget}>
            <EditWidgetFrame onCancel={() => {}} onSubmit={() => Promise.resolve()} {...props}>
              Hello World! These are some buttons!
            </EditWidgetFrame>
          </WidgetContext.Provider>
        </TestStoreProvider>,
      );

    useViewsPlugin();

    it('refreshes search after clicking on search button, when there are no changes', async () => {
      renderSUT();
      const searchButton = await screen.findByRole('button', { name: /perform search/i });

      await waitFor(() => expect(searchButton).not.toHaveClass('disabled'));
      await userEvent.click(searchButton);

      await waitFor(() => expect(executeActiveQuery).toHaveBeenCalledTimes(1));
    });

    it("changes the widget's streams when using stream filter", async () => {
      renderSUT();
      const streamFilter = await screen.findByTestId('streams-filter');
      const reactSelect = streamFilter.querySelector('div');

      expect(reactSelect).not.toBeNull();

      if (reactSelect) {
        await selectEvent.select(reactSelect, 'PFLog');
      }

      const searchButton = screen.getByRole('button', {
        name: /perform search \(changes were made after last search execution\)/i,
      });
      await waitFor(() => expect(searchButton).not.toHaveClass('disabled'));

      await userEvent.click(searchButton);

      await waitFor(() =>
        expect(updateWidget).toHaveBeenCalledWith(
          'deadbeef',
          expect.objectContaining({
            streams: ['5c2e27d6ba33a9681ad62775'],
          }),
        ),
      );
    });

    it('calls onSubmit', async () => {
      const onSubmit = jest.fn(() => Promise.resolve());
      renderSUT({ onSubmit });

      await userEvent.click(await screen.findByRole('button', { name: /update widget/i }));

      await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
    });

    it('calls onCancel', async () => {
      const onCancel = jest.fn();
      renderSUT({ onCancel });

      await userEvent.click(await screen.findByRole('button', { name: /cancel/i }));

      await waitFor(() => expect(onCancel).toHaveBeenCalledTimes(1));
    });

    it('does not display submit and cancel button when `displaySubmitActions` is false', async () => {
      renderSUT({ displaySubmitActions: false });

      expect(screen.queryByRole('button', { name: /update widget/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('button', { name: /cancel/i })).not.toBeInTheDocument();
    });
  });
});
